#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "ht.h"
#include "htlog.h"

struct htcon * htcons;
struct htcon * lastcon;

/*start listening on ip:port
  return listening socket fd if success
  return -1 if error
*/
int htlisten(char * ip, unsigned short port)
{
  int sk;
  struct linger linger = {1, 5};
  struct sockaddr_in saddr;
  
  int flag=1;
  
  if((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    LOGERR("create listen socket failed. errno:%d(%s)");
    return -1;
  }
    
  if(setsockopt(sk, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(struct linger))) {
    LOGERR("set listen socket SO_LINGER failed. errno:%d(%s)");
    close(sk);
    return -1;
  }
  if(setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, sizeof(flag))) {
    LOGERR("set listen socket SO_REUSEADDR failed. errno:%d(%s)");
    close(sk);
    return -1;
  }

  if(fcntl(sk, F_SETFL, O_NONBLOCK)) {
    LOGERR("set listen socket O_NONBLOCK failed. errno:%d(%s)");
    close(sk);
    return -1;
  }
  
  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_port = htons(port);
  saddr.sin_family = AF_INET;
  if(!inet_aton(ip, (struct in_addr *)&saddr.sin_addr.s_addr)) {
    LOGERR("inet_aton failed. errno:%d(%s)");
    close(sk);
    return -1;
  }
  
  if(bind(sk, (struct sockaddr *)&saddr, sizeof(saddr))) {
    LOGERR("bind failed. errno:%d(%s)");
    close(sk);
    return -1;
  }
  
  if(listen(sk, 32)) {
    LOGERR("listen failed. errno:%d(%s)");
    close(sk);
    return -1;
  }
  LOG("listening on %s:%d", ip, port);
  return sk;
}

/*connect to server addr:port
return socket if success
return -1 if failed
*/
int htconnect(struct sockaddr_in * addr, short port)
{
  int sk;
  struct linger linger = {1, 5};
  
  int flag=1;
  if((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    LOGERR("create to server socket failed. errno:%d(%s)");
    return -1;
  }
    
  if(setsockopt(sk, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(struct linger))) {
    LOGERR("set to server socket SO_LINGER failed. errno:%d(%s)");
    close(sk);
    return -1;
  }
  if(setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, sizeof(flag))) {
    LOGERR("set to server socket SO_REUSEADDR failed. errno:%d(%s)");
    close(sk);
    return -1;
  }

  if(fcntl(sk, F_SETFL, O_NONBLOCK)) {
    LOGERR("set to server socket O_NONBLOCK failed. errno:%d(%s)");
    close(sk);
    return -1;
  }
  
  if(connect(sk, (struct sockaddr *)addr, sizeof(struct sockaddr))) {
    if(EINPROGRESS != errno) {
      LOGERR("connect to server failed. errno:%d(%s)");
      close(sk);
      return -1;}
    return sk;
  }
  return sk;
}
/*create new connection and add to connections list
  return 0 if success
  return -1 if error
*/
int htnewconnect(int cfd, struct sockaddr_in *caddr)
{
  struct htcon * con;

  con = (struct htcon *)calloc(sizeof(struct htcon), 1);
  if(!con) {
    LOGERR("error while malloc memory for connect. erron:%d(%s).");
    return -1;
  }
  strncpy(con->caddr, inet_ntoa(caddr->sin_addr), sizeof(con->caddr));
  con->cport = caddr->sin_port;
  con->cfd = cfd;
  con->sfd = -1;
  con->bufsize = HTCON_BUF_SIZE;
  con->upbuf = malloc(HTCON_BUF_SIZE);
  con->downbuf = malloc(HTCON_BUF_SIZE);
  con->status = HTCON_STAT_CLIENT_CONNECTED;

  /*add to con list*/
  if(htcons) {
    con->next = htcons->next;
    con->pre = htcons;
    if(htcons->next) htcons->next->pre = con;
    htcons->next = con;
  } else {
    /*empty list*/
    htcons = con;
  }
  LOG("client %s:%d connected.", con->caddr, con->cport);
  return 0;
}
/*close a two way connection and remove from connections list
  return 1 if two way is closed
  return 0 if one way is closed
 */
int htclose(struct htcon *con, uint flag)
{
  if(flag & HT_CLOSE_CLIENT) {
    SHUT_FD(con->cfd);
    if(con->cswpos-con->csrpos<=0){
      SHUT_FD(con->sfd);
    }      
  }
  if(flag & HT_CLOSE_SERVER) {
    SHUT_FD(con->sfd);
    if(con->scwpos-con->scrpos<=0){
      SHUT_FD(con->cfd);
    }      
  }

  if(con->cfd<0 && con->sfd<0) {
    if(con->upbuf) {
      free(con->upbuf);
      con->upbuf = 0;
    }
    if(con->downbuf) {
      free(con->downbuf);
      con->downbuf = 0;
    }
    if(con->pre) con->pre->next = con->next;
    if(con->next) con->next->pre = con->pre;
    if(con == htcons) htcons = con->next;
    free(con);
    return 0;
  }else{
    return 1;
  }
}

int handle_post_upread(struct htcon * con)
{
  int len;      /*avaliable data length in upbuf*/
  uchar * data;

  /*if buffer is to full, move to head*/
  if(con->cswpos == con->bufsize && con->csrpos>0 && con->csrpos < con->cswpos) {
    /*move a block from high address to low address, should be safe*/
    memcpy(con->upbuf, con->upbuf + con->csrpos, con->cswpos-con->csrpos);
    con->cswpos = con->cswpos - con->csrpos;
    con->csrpos = 0;
  }
  len = con->cswpos - con->csrpos;
  data = con->upbuf + con->csrpos;

  /*handle auth*/
  if(con->status == HTCON_STAT_CLIENT_CONNECTED && len>2){
    if(len-2>=data[1]) {
      /*auth package recved*/
      /*discard readed data*/
      con->cswpos = con->csrpos;
      if(data[0] != '\x05') {
	/*version is not 5*/
	con->scrpos = 0;
	con->scwpos = 2;
	con->downbuf[0] = '\x05';
	con->downbuf[1] = '\xFF';
	con->status = HTCON_STAT_CLOSING;
	return HT_VERSION_NOT_SUPPORTED;
      }
      int i;
      for(i=data[1];i>0 && data[i+1];--i);
      if(!i) {
	/*method not acceptable*/
	con->scrpos = 0;
	con->scwpos = 2;
	con->downbuf[0] = '\x05';
	con->downbuf[1] = '\xFF';
	con->status = HTCON_STAT_CLOSING;
	return HT_AUTH_NOT_SUPPORTED;
      }
      /*auth method accepted*/
      con->scrpos = 0;
      con->scwpos = 2;
      con->downbuf[0] = '\x05';
      con->downbuf[1] = '\x00';
      con->status = HTCON_STAT_CLIENT_AUTHED;
      return HT_SUCCESS;
    }
  }

  /*handle cmd*/
  if(con->status == HTCON_STAT_CLIENT_AUTHED && len>4){
   uchar atyp = data[3];
    uchar addrlen;
    if(atyp!='\x01' && atyp!='\x03') {
      /*discard readed data*/
      con->cswpos = con->csrpos;
      con->scrpos = 0;
      con->scwpos = 10;
      memcpy(con->downbuf, data, 4);
      memset(con->downbuf+4, 0, 6);
      con->status = HTCON_STAT_CLOSING;
      return HT_ADDR_NOT_SUPPORTED;
    }

    addrlen = ( atyp=='\x01' ? 4 : data[4]+1 );
    if(len >= 6+addrlen) {
      /* prepare for reply data*/
      con->scrpos = 0;
      con->scwpos = 6 + addrlen;
      memcpy(con->downbuf, data, 6+addrlen);

      ushort port;
      struct sockaddr_in serveraddr;
      memset(&serveraddr, 0, sizeof(serveraddr));
      if(atyp=='\x01') {
	serveraddr.sin_addr.s_addr = *(uint32_t*)(data+4);
	port = ntohs(*(ushort*)(data+8));
      } else if(atyp=='\x03') {
	struct hostent * host;
	char addr[256];
	memset(addr, 0, sizeof(addr));
	memcpy(addr, &data[5], addrlen-1);
	/*FIXME: DNS resolve should be non-blocking */
	if(!(host = gethostbyname(addr))) {
	  /*discard readed data*/
	  con->cswpos = con->csrpos;
	  con->downbuf[1] = '\x04';    /*REP:Host unreachable*/
	  con->status = HTCON_STAT_CLOSING;
	  return HT_DNS_FAIL;
	} else {
	  serveraddr.sin_addr.s_addr = *(uint32_t*)(host->h_addr);
	}
	port = ntohs(*(ushort*)(data+addrlen+4));
      }
      /*try to connect*/
      serveraddr.sin_port = htons(port);
      serveraddr.sin_family = AF_INET;
      if((con->sfd = htconnect(&serveraddr, port)) < 0){
	/*discard readed data*/
	con->cswpos = con->csrpos;
	con->downbuf[1] = '\x04';    /*REP:Host unreachable*/
	con->status = HTCON_STAT_CLOSING;
	return HT_CONNECT_FAIL;
      }
      memcpy(con->saddr, inet_ntoa(serveraddr.sin_addr), sizeof(con->saddr)-1);
      con->sport = port;
      /* reply no data*/
      con->scrpos = 0;
      con->scwpos = 0;
      con->status = HTCON_STAT_CLIENT_READY;
      return HT_SUCCESS;
    }
  }

  return HT_SUCCESS;
}

int handle_pre_upwrite(struct htcon * con)
{
  if(con->status == HTCON_STAT_CLIENT_READY) {
    /*reply to client*/
    con->scrpos = 0;
    con->scwpos = con->cswpos - con->csrpos;
    memcpy(con->downbuf, &con->upbuf[con->csrpos], con->cswpos-con->csrpos);
    int optval;
    socklen_t optlen;
    getsockopt(con->sfd, SOL_SOCKET, SO_ERROR, &optval, &optlen);
    if(optval) {
      /*error while connect*/
      con->downbuf[1] = '\x04';    /*REP:Host unreachable*/
      con->status = HTCON_STAT_CLOSING;
      return HT_CONNECT_FAIL;
    } else {
      con->downbuf[1] = '\x00';    /*REP:succeeded */
      /*discard readed data*/
      con->cswpos = con->csrpos;
      /*connect successed*/
      con->status = HTCON_STAT_READY;
      return HT_NODATA;
    }
  }

  return HT_SUCCESS;
}

uint get_fd_flag(struct htcon * con)
{
  uint flag = 0;
  if(con->cfd >= 0 && con->cswpos < con->bufsize && 
     (con->status == HTCON_STAT_READY ||
      con->status == HTCON_STAT_CLIENT_CONNECTED ||
      con->status == HTCON_STAT_CLIENT_AUTHING ||
      con->status == HTCON_STAT_CLIENT_AUTHED ||
      con->status == HTCON_STAT_CLIENT_READY)
     ) {
    flag = flag | HT_CFD_READ;
  }
  /*if s->c buffer is readable, add to write watch*/
  if(con->cfd >=0 && con->scrpos < con->scwpos) {
    flag = flag | HT_CFD_WRITE;
  }
  /*for OOB*/
  if(con->cfd >=0 && !con->coobflg) {
    flag = flag | HT_CFD_OOB;
  }
      
  /*server socket*/
  /*if s->c buffer is writable, add to read watch*/
  if(con->sfd >=0 && con->scwpos < con->bufsize) {
    flag = flag | HT_SFD_READ;
  }
  /*if c->s buffer is readable or connected just now, add to write watch*/
  if(con->sfd >=0 && (con->csrpos < con->cswpos || con->status==HTCON_STAT_CLIENT_READY)) {
    flag = flag | HT_SFD_WRITE;
  }
  /*for OOB*/
  if(con->sfd >=0 && !con->soobflg) {
    flag = flag | HT_SFD_OOB;
  }
  return flag;
}
/*run server on socket fd lfd
  return 0 if success
  return -1 if error
*/
int runserver(int lfd)
{
  int nfds=0;
  int r;
  fd_set rd, wr, er;
  struct htcon *con, *tmp;

  while(1) {
    nfds = -1;
    FD_ZERO(&rd);
    FD_ZERO(&wr);
    FD_ZERO(&er);

    /*add listening socket for waiting accept*/
    FD_SET(lfd, &rd);
    nfds = max(nfds, lfd);

    con = htcons;
    while(con) {
      /*client socket*/
      /*if c->s buffer is writable, add to read watch*/
      uint fdflag;
      fdflag = get_fd_flag(con);
      if(HT_CFD_READ & fdflag) {
	FD_SET(con->cfd, &rd);
	nfds = max(nfds, con->cfd);
      }
      /*if s->c buffer is readable, add to write watch*/
      if(HT_CFD_WRITE & fdflag) {
	FD_SET(con->cfd, &wr);
	nfds = max(nfds, con->cfd);
      }
      /*for OOB*/
      if(HT_CFD_OOB & fdflag) {
	FD_SET(con->cfd, &er);
	nfds = max(nfds, con->cfd);
      }
      
      /*server socket*/
      /*if s->c buffer is writable, add to read watch*/
      if(HT_SFD_READ & fdflag) {
	FD_SET(con->sfd, &rd);
	nfds = max(nfds, con->sfd);
      }
      /*if c->s buffer is readable or connected just now, add to write watch*/
      if(HT_SFD_WRITE & fdflag) {
	FD_SET(con->sfd, &wr);
	nfds = max(nfds, con->sfd);
      }
      /*for OOB*/
      if(HT_SFD_OOB & fdflag) {
	FD_SET(con->sfd, &er);
	nfds = max(nfds, con->sfd);
      }
      
      /*next connection*/
      con = con->next;
    }
    /*wait for io*/
    r = select(nfds+1, &rd, &wr, &er, 0);
    if(r == -1 && errno == EINTR && errno != EAGAIN) continue;
    if(r < 0) {
      LOGERR("select failed. errno:%d(%s)");
      return -1;
    }
    /*new connection*/
    if(FD_ISSET(lfd, &rd)) {
      unsigned int l;
      struct sockaddr_in caddr;
      int cfd;
      memset(&caddr, 0, l=sizeof(caddr));
      cfd = accept(lfd, (struct sockaddr *)&caddr, (socklen_t *)&l);
      htnewconnect(cfd, &caddr);
    }
    con = htcons;
    while(con) {
      /*then OOB data from client*/
      if(con->cfd >=0 && (con->coobflg || FD_ISSET(con->cfd, &er))) {
	if(!con->coobflg) {
	  /*if oob is recved in last round, con->cfd was not added to &er*/
	  r = recv(con->cfd, &con->coob, 1, MSG_OOB);
	  if(r==0) {
	    LOG("eof while recv oob data from client, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  } else if(r==1) {
	    con->coobflg = 1;
	  } else if(errno != EINTR && errno != EAGAIN) {
	    LOGERR("recv oob data from client failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  }/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
	}
	if(con->coobflg) {
	  r = send(con->sfd, &con->coob, 1, MSG_OOB);
	  if(r==0) {
	    LOG("eof while send oob data to server, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  } else if(r==1) {
	    con->coobflg = 0;
	  } else if(errno != EINTR && errno != EAGAIN) {
	    LOGERR("send oob data to server failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  }/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
	}
      }
      /*then OOB data from server*/
      if(con->sfd >=0 && (con->soobflg || FD_ISSET(con->sfd, &er))) {
	if(!con->soobflg) {
	  /*if oob is recved in last round, con->sfd was not added to &er*/
	  r = recv(con->sfd, &con->soob, 1, MSG_OOB);
	  if(r==0) {
	    LOG("eof while recv oob data from server, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  } else if(r==1) {
	    con->soobflg = 1;
	  } else if(errno != EINTR && errno != EAGAIN) {
	    LOGERR("recv oob data from server failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  }/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
	}
	if(con->soobflg) {
	  r = send(con->cfd, &con->soob, 1, MSG_OOB);
	  if(r==0) {
	    LOG("eof while send oob data to client, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  } else if(r==1) {
	    con->soobflg = 0;
	  } else if(errno != EINTR && errno != EAGAIN) {
	    LOGERR("send oob data to client failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  }/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
	}
      }

      /*if client socket is readable*/
      if(con->cfd>=0 && FD_ISSET(con->cfd, &rd)) {
	r = recv(con->cfd, con->upbuf+con->cswpos, con->bufsize-con->cswpos, 0);
	if(r==0) {
	  LOG("eof while recv data from client, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	  tmp = con->next;
	  con->status = HTCON_STAT_CLOSING;
	  if(htclose(con, HT_CLOSE_CLIENT)) {
	    con = tmp;
	    continue;
	  }
	} else if(r>0) {
	  con->cswpos += r;
	  handle_post_upread(con);
	} else if(r<0 && errno != EINTR && errno != EAGAIN) {
	  LOGERR("recv data from client failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	  tmp = con->next;
	  con->status = HTCON_STAT_CLOSING;
	  if(htclose(con, HT_CLOSE_CLIENT | HT_CLOSE_SERVER)) {
	    con = tmp;
	    continue;
	  }
	}/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
      }
      /*if server socket is readable*/
      if(con->sfd >=0 && FD_ISSET(con->sfd, &rd)) {
	r = recv(con->sfd, con->downbuf+con->scwpos, con->bufsize-con->scwpos, 0);
	if(r==0) {
	  LOG("eof while recv data from server, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	  tmp = con->next;
	  con->status = HTCON_STAT_CLOSING;
	  if(htclose(con, HT_CLOSE_SERVER)) {
	    con = tmp;
	    continue;
	  }
	} else if(r>0) {
	  con->scwpos += r;
	} else if(r<0 && errno != EINTR && errno != EAGAIN) {
	  LOGERR("recv data from server failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	  tmp = con->next;
	  con->status = HTCON_STAT_CLOSING;
	  if(htclose(con, HT_CLOSE_SERVER)) {
	    con = tmp;
	    continue;
	  }
	}/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
      }
      /*if client socket is writable*/
      if(con->cfd>=0 && FD_ISSET(con->cfd, &wr)) {
	r = send(con->cfd, con->downbuf+con->scrpos, con->scwpos-con->scrpos, 0);
	if(r==0) {
	  LOG("eof while send data to client, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	  tmp = con->next;
	  con->status = HTCON_STAT_CLOSING;
	  if(htclose(con, HT_CLOSE_CLIENT)) {
	    con = tmp;
	    continue;
	  }
	} else if(r>0) {
	  con->scrpos += r;
	  if(con->scrpos == con->scwpos) {
	    con->scrpos = 0;
	    con->scwpos = 0;
	    if(con->status == HTCON_STAT_CLOSING) {
	      /*all data writed, close*/
	      tmp = con->next;
	      if(htclose(con, HT_CLOSE_CLIENT)) {
		con = tmp;
		continue;
	      }
	    }
	  }
	} else if(r<0 && errno != EINTR && errno != EAGAIN) {
	  LOGERR("send data to client failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	  tmp = con->next;
	  con->status = HTCON_STAT_CLOSING;
	  if(htclose(con, HT_CLOSE_CLIENT)) {
	    con = tmp;
	    continue;
	  }
	}/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
      }
      /*if server socket is writable*/
      if(con->sfd >=0 && FD_ISSET(con->sfd, &wr)) {
	if(!handle_pre_upwrite(con)) {
	  r = send(con->sfd, con->upbuf+con->csrpos, con->cswpos-con->csrpos, 0);
	  if(r==0) {
	    LOG("eof while send data to server, close connection. client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    con->status = HTCON_STAT_CLOSING;
	    if(htclose(con, HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  } else if(r>0) {
	    con->csrpos += r;
	    if(con->csrpos == con->cswpos) {
	      con->csrpos = 0;
	      con->cswpos = 0;
	      if(con->status == HTCON_STAT_CLOSING) {
		/*all data writed, close*/
		tmp = con->next;
		if(htclose(con, HT_CLOSE_SERVER)) {
		  con = tmp;
		  continue;
		}
	      }
	    }
	  } else if(r<0 && errno != EINTR && errno != EAGAIN) {
	    LOGERR("send data to server failed. errno:%d(%s).client %s:%d server %s:%d", con->caddr, con->cport, con->saddr, con->sport);
	    tmp = con->next;
	    if(htclose(con, HT_CLOSE_SERVER)) {
	      con = tmp;
	      continue;
	    }
	  }/* else { errno==EINTR || errno == EAGAIN do nothing, try in next round } */
	}
      }
	    
      /*next connection*/
      con = con->next;
    }
  }
}

int main()
{
  int lfd;
  
  htlogflg = 1;
  if(!htlog_init("/tmp/ht.log")) return 1;

  /*deamonize();*/

  if((lfd=htlisten("127.0.0.1", 1080))<0) return EXIT_FAILURE;
  
  runserver(lfd);
  
  return 0;
  
}
